<template>
  <el-dialog
    v-model="dialogVisible"
    :title="isNew ? '新建菜单' : '修改菜单'"
    width="650px"
    :before-close="close"
    :close-on-click-modal="false"
  >
    <div v-loading="loading">
      <el-form ref="formRef" :model="formData" label-width="110" :rules="rules">
        <el-form-item
          label="菜单类型："
          prop="menuType"
          @change="formRef.clearValidate()"
        >
          <el-radio-group v-model="formData.menuType">
            <el-radio-button label="目录" value="M" />
            <el-radio-button label="菜单" value="C" />
            <el-radio-button label="按钮" value="F" />
          </el-radio-group>
        </el-form-item>
        <template v-if="formData.menuType === 'M'">
          <el-row :gutter="10">
            <el-col :span="12">
              <el-form-item label="菜单标题：" prop="name">
                <el-input v-model="formData.name" />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="菜单可见：" prop="hidden">
                <el-switch
                  v-model="formData.hidden"
                  active-text="是"
                  inactive-text="否"
                  :active-value="0"
                  :inactive-value="1"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-form-item label="图标：">
            <el-select-v2
              v-model="formData.icon"
              filterable
              clearable
              :options="bootstrapIconList"
            >
              <template #default="{ item }">
                <template v-if="item.value.indexOf('bi-') !== -1">
                  <i :class="item.value" />
                  <span class="px-2">{{ item.label }}</span>
                </template>
              </template>
              <template #label="{ label, value }">
                <template v-if="value.indexOf('bi-') !== -1">
                  <i :class="value" />
                  <span class="px-2">{{ label }}</span>
                </template>
              </template>
            </el-select-v2>
          </el-form-item>
          <el-row :gutter="10">
            <el-col :span="12">
              <el-form-item label="路由地址：" prop="path">
                <el-input v-model="formData.path" />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="菜单排序：" prop="orderNum">
                <el-input-number
                  class="order-num-input"
                  v-model="formData.orderNum"
                  :max="999"
                  :precision="0"
                  controls-position="right"
                />
              </el-form-item>
            </el-col>
          </el-row>
        </template>
        <template v-if="formData.menuType === 'C'">
          <el-row :gutter="10">
            <el-col :span="12">
              <el-form-item label="菜单标题：" prop="name">
                <el-input v-model="formData.name" />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="菜单可见：" prop="hidden">
                <el-switch
                  v-model="formData.hidden"
                  active-text="是"
                  inactive-text="否"
                  :active-value="0"
                  :inactive-value="1"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="10">
            <el-col :span="12">
              <el-form-item label="图标：">
                <el-select-v2
                  v-model="formData.icon"
                  filterable
                  clearable
                  :options="bootstrapIconList"
                >
                  <template #default="{ item }">
                    <template v-if="item.value.indexOf('bi-') !== -1">
                      <i :class="item.value" />
                      <span class="px-2">{{ item.label }}</span>
                    </template>
                  </template>
                  <template #label="{ label, value }">
                    <template v-if="value.indexOf('bi-') !== -1">
                      <i :class="value" />
                      <span class="px-2">{{ label }}</span>
                    </template>
                  </template>
                </el-select-v2>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="权限标识：" prop="perms">
                <el-input v-model="formData.perms" />
              </el-form-item>
            </el-col>
          </el-row>
          <el-row :gutter="10">
            <el-col :span="12">
              <el-form-item label="路由地址：" prop="path">
                <el-input v-model="formData.path" />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="菜单排序：" prop="orderNum">
                <el-input-number
                  class="order-num-input"
                  v-model="formData.orderNum"
                  :max="999"
                  :precision="0"
                  controls-position="right"
                />
              </el-form-item>
            </el-col>
          </el-row>
          <el-form-item label="组件路径：" prop="component">
            <el-input v-model="formData.component" />
          </el-form-item>
        </template>
        <template v-if="formData.menuType === 'F'">
          <el-form-item label="按钮名称：" prop="name">
            <el-input v-model="formData.name" />
          </el-form-item>
          <el-row :gutter="10">
            <el-col :span="12">
              <el-form-item label="权限标识：" prop="perms">
                <el-input v-model="formData.perms" />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="菜单排序：" prop="orderNum">
                <el-input-number
                  class="order-num-input"
                  v-model="formData.orderNum"
                  :max="999"
                  :precision="0"
                  controls-position="right"
                />
              </el-form-item>
            </el-col>
          </el-row>
        </template>
        <el-form-item label="上级类目：" prop="parentId">
          <el-cascader
            v-model="formData.parentId"
            :props="props"
            :options="cascaderOption"
            :show-all-levels="false"
            placeholder="请选择"
          />
        </el-form-item>
        <el-row :gutter="10">
          <el-col :span="12">
            <el-form-item label="仅超管可见：" prop="disabled">
              <el-switch
                v-model="formData.disabled"
                active-text="是"
                inactive-text="否"
                :active-value="1"
                :inactive-value="0"
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
    </div>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="close">取消</el-button>
        <el-button type="primary" @click="submit" :loading="loading">
          提交
        </el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup lang="ts">
import bootstrapIcon from 'bootstrap-icons/font/bootstrap-icons.json'
import { computed, reactive, ref, toRaw, useTemplateRef } from 'vue'
import {
  CascaderProps,
  ElSelectV2,
  FormInstance,
  FormRules,
} from 'element-plus'
import { saveMenu } from '@/api/sys/menu'
import { Message } from '@/hooks/message'

const formRef = useTemplateRef<FormInstance>('formRef')
const emit = defineEmits(['update'])

const bootstrapIconList = computed(() => {
  return Object.keys(bootstrapIcon).map((key) => {
    return {
      label: key,
      value: 'bi-' + key,
    }
  })
})

const { parentTree } = defineProps<{
  parentTree: any[]
}>()

const isNew = computed(() => {
  return !formData.id
})

const cascaderOption = computed(() => {
  return [
    {
      id: 0,
      name: '顶级目录',
      menuType: 'M',
      children: parentTree,
    },
  ]
})

const dialogVisible = ref(false)
const loading = ref(false)
const formData = reactive({
  id: undefined,
  menuType: 'M',
  icon: undefined,
  name: undefined,
  path: undefined,
  component: undefined,
  disabled: 0,
  perms: undefined,
  orderNum: 999,
  parentId: 0,
  hidden: 0,
  type: undefined,
})

const props: CascaderProps = {
  checkStrictly: true,
  emitPath: false,
  value: 'id',
  label: 'name',
}

const rules = computed<FormRules<typeof formData>>(() => {
  if (formData.menuType === 'M') {
    return {
      name: { required: true, message: '请输入菜单标题', trigger: 'blur' },
      type: { required: true, message: '请选择终端类型', trigger: 'change' },
      parentId: { validator: menuVerification, trigger: 'change' },
    }
  } else if (formData.menuType === 'C') {
    return {
      name: { required: true, message: '请输入菜单标题', trigger: 'blur' },
      perms: { required: true, message: '请输入权限标识', trigger: 'blur' },
      path: { required: true, message: '请输入路由地址', trigger: 'blur' },
      component: { required: true, message: '请输入组件路径', trigger: 'blur' },
      type: { required: true, message: '请选择终端类型', trigger: 'change' },
      parentId: { validator: menuVerification, trigger: 'change' },
    }
  } else if (formData.menuType === 'F') {
    return {
      name: { required: true, message: '请输入按钮名称', trigger: 'blur' },
      type: { required: true, message: '请选择终端类型', trigger: 'change' },
      parentId: { validator: buttonVerification, trigger: 'change' },
    }
  } else {
    return {}
  }
})

const buttonVerification = (rule: any, value: any, callback: any) => {
  const node = getTreeNodeById(value, cascaderOption.value)
  if (node) {
    if (node.menuType === 'F') {
      callback(new Error('按钮不能作为上级目录'))
      // 如果是管理端，则不能将按钮放在顶级
    } else if (formData.type === '2' && node.menuType === 'M') {
      callback(new Error('目录不能作为按钮的上级目录'))
    } else {
      callback()
    }
  }
}

const menuVerification = (rule: any, value: any, callback: any) => {
  const node = getTreeNodeById(value, cascaderOption.value)
  if (node) {
    if (node.menuType === 'F') {
      callback(new Error('按钮不能作为上级目录'))
    } else if (node.menuType === 'C') {
      callback(new Error('菜单不能作为上级目录'))
    } else if (node.id === formData.id) {
      callback(new Error('上级目录不能选择自己'))
    } else {
      callback()
    }
  }
}

/**
 * 递归查询节点
 * @param id 节点id
 * @param tree 树形结构
 */
function getTreeNodeById(id: number, tree: any[]) {
  for (let i = 0; i < tree.length; i++) {
    if (tree[i].id === id) {
      return tree[i]
    }
    if (tree[i].children && tree[i].children.length > 0) {
      const result = getTreeNodeById(id, tree[i].children)
      if (result) {
        return result
      }
    }
  }
}

defineExpose({ show })

function show(row: any) {
  dialogVisible.value = true
  if (row) {
    if (typeof row === 'number') {
      formData.parentId = row
    } else if (typeof row === 'object') {
      formData.id = row.id
      formData.menuType = row.menuType
      formData.icon = row.icon
      formData.name = row.name
      formData.path = row.path
      formData.disabled = row.disabled
      formData.component = row.component
      formData.perms = row.perms
      formData.orderNum = row.orderNum
      formData.parentId = row.parentId
      formData.hidden = row.hidden
      formData.type = row.type
    }
  }
}

function close() {
  dialogVisible.value = false
  loading.value = false
  formRef.value.resetFields()
  Object.assign(formData, {
    id: undefined,
    menuType: 'M',
    icon: undefined,
    name: undefined,
    path: undefined,
    component: undefined,
    perms: undefined,
    disabled: 0,
    orderNum: 999,
    parentId: 0,
    hidden: 0,
  })
}

async function submit() {
  const bool = await formRef.value.validate()
  if (!bool) {
    return
  }
  loading.value = true
  const oldFormData = toRaw(formData)
  const postData = {
    ...oldFormData,
    icon: oldFormData.icon ? oldFormData.icon : '',
  }
  const { data } = await saveMenu(postData)
  if (data.status === 200) {
    Message({
      message: data.message,
      type: 'success',
    })
  }
  emit('update')
  close()
}
</script>
<style scoped lang="scss">
.order-num-input {
  @apply w-full;
}
</style>
